﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Threading;
using Microsoft.Win32;

using CommunicatorAPI;

using Microsoft.Samples.Office.UnifiedCommunications.Gadget.Config;
using Microsoft.Samples.Office.UnifiedCommunications.Gadget.DTO;
using Microsoft.Samples.Office.UnifiedCommunications.Gadget.Events;
using Microsoft.Samples.Office.UnifiedCommunications.Gadget.EWSUtility;

namespace Microsoft.Samples.Office.UnifiedCommunications.Gadget
{   
    public partial class Gadget : Page
    { 
        #region Variables

        private static Gadget _instance = null;

        // Timers
        //
        private DispatcherTimer _timerVoicemail = null;
        private DispatcherTimer _timerEmailCounts = null;

        // Instance variables
        //
        private static SettingsEWS _curSettings;

        // Delegates
        //
        public delegate void LoadFav5Delegate();
        public delegate void Fav5ChangedDelegate(Fav5ChangedEventArgs e);
        public delegate void DesktopContactsChangedDelegate(DesktopContactChangedEventArgs e);
        public delegate void LoadTop5Delegate();
        public delegate void GenericUIDelegate();
        public delegate void ProcessContactStatusChangeDelegate(object pMContact);

        // Instances of Windows used by the Gadget
        //
        private SettingsWindow _configWindow;
        private VoiceMailWindow _voiceMailWindow;

        private const string ERROR_COMMUNICATOR_NOT_INSTALLED = "Communicator is not installed";

        #endregion

        #region Properties

        public static Gadget Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new Gadget();
                    _instance.InitializeComponent();
                }
                return _instance;
            }
        }

        public static SettingsEWS CurrentSettings
        {
            get
            {
                return _curSettings;
            }
            set
            {
                _curSettings = value;
                GadgetConfigurationManager.SetUserSettingsEWS(_curSettings);
            }
        }

        public static string DisplayName { get { return _myDisplayName; } }

        public IDictionary<String, IntPtr> UserDesktopContacts
        {
            get { return (IDictionary<String, IntPtr>)GetValue(UserDesktopContactsProperty); }
            set { SetValue(UserDesktopContactsProperty, value); }
        }

        public static readonly DependencyProperty UserDesktopContactsProperty =
            DependencyProperty.Register(
                "UserDesktopContacts",
                typeof(IDictionary<String, IntPtr>),
                typeof(Gadget),
                new FrameworkPropertyMetadata(new PropertyChangedCallback(OnUserDesktopContactsChanged)));

        private static void OnUserDesktopContactsChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            (o as Gadget).UpdateUserDesktopContacts(e.NewValue as IDictionary<String, IntPtr>);
        }

        public List<String> PresenceHistoryWindows
        {
            get { return (List<String>)GetValue(PresenceHistoryWindowsProperty); }
            set { SetValue(PresenceHistoryWindowsProperty, value); }
        }

        public static readonly DependencyProperty PresenceHistoryWindowsProperty =
            DependencyProperty.Register(
                "PresenceHistoryWindows",
                typeof(List<String>),
                typeof(Gadget));

        #endregion

        public Gadget()
        {
            _instance = this;

            InitializeComponent();

            raiseUnreadVoicemailEvent = new RaiseUnreadVoicemailEventDelegate(RaiseUnreadVoicemailEvent);

            GadgetEventManager.Instance.UnreadVoicemailEvent += new EventHandler(Instance_UnreadVoicemailEvent);
            GadgetEventManager.Instance.EWSConfiguredEvent += new EventHandler(Instance_EWSConfiguredEvent);
            GadgetEventManager.Instance.Fav5ChangedEvent += new EventHandler(Instance_Fav5ChangedEvent);
            GadgetEventManager.Instance.Top5ChangedEvent += new EventHandler(Instance_Top5ChangedEvent);
            GadgetEventManager.Instance.GadgetSignInEvent += new EventHandler(Instance_GadgetSignInEvent);
            GadgetEventManager.Instance.VoicemailEnabledEvent += new EventHandler(Instance_VoicemailEnabledEvent);
            GadgetEventManager.Instance.DesktopContactChangedEvent += new EventHandler(Instance_DesktopContactChangedEvent);

            this.expanderFav5.Collapsed += new RoutedEventHandler(expanderFav5_Collapsed);
            this.expanderFav5.Expanded += new RoutedEventHandler(expanderFav5_Expanded);
            this.expanderTop5.Collapsed += new RoutedEventHandler(expanderTop5_Collapsed);
            this.expanderTop5.Expanded += new RoutedEventHandler(expanderTop5_Expanded);
            this.configGrid.MouseDown += new MouseButtonEventHandler(configGrid_MouseDown);
            this.phoneLayer.MouseDown += new MouseButtonEventHandler(phoneLayer_MouseDown);
            this.listBoxTop5.PreviewMouseDown += new MouseButtonEventHandler(listBoxTop5_PreviewMouseDown);
            this.listBoxFav5.PreviewMouseDown += new MouseButtonEventHandler(listBoxFav5_PreviewMouseDown);
            this.listBoxTop5.MouseDoubleClick += new MouseButtonEventHandler(listBoxTop5_MouseDoubleClick);
            this.listBoxFav5.MouseDoubleClick += new MouseButtonEventHandler(listBoxFav5_MouseDoubleClick);
            this.Unloaded += new RoutedEventHandler(Gadget_Unloaded);

            SystemEvents.PowerModeChanged += new PowerModeChangedEventHandler(SystemEvents_PowerModeChanged);
        }

        #region Loading

        void Page_Loaded(object sender, RoutedEventArgs e)
        {
            if (_communicator != null && _connected && !String.IsNullOrEmpty(_mySipUri))
                return;

            try
            {
                _communicator = new CommunicatorAPI.MessengerClass();
                _communicatorAdvanced = _communicator as IMessengerAdvanced;
                _resolver = _communicator as IMessengerContactResolution;

                _communicator.OnSignin += new DMessengerEvents_OnSigninEventHandler(communicator_OnSignin);
            }
            catch (Exception)
            {
                throw;
            }
        }

        void InitializeGadget()
        {
            if (EWSConfiguration.IsEWSConfigured())
            {
                gridMyPersona.Visibility = Visibility.Visible;
                gridEWSConfig.Visibility = Visibility.Collapsed;

                configGrid.Visibility = Visibility.Visible; // Make visible after successful sign-in
                phoneLayer.Visibility = Visibility.Visible; // Make visible if VoiceMail is enabled

                // Get the current user settings
                _curSettings = GadgetConfigurationManager.GetUserSettingsEWS();

                SetupTimers();                  // Only fire off timer jobs if the gadget has been configured
                LoadUserDetails();              // Load user's status and display name
                LoadFav5();                     // Load the user's Fav5 contacts
                LoadTop5();                     // Load the user's Top5 contacts
                LoadDesktopContactWindows();    // Load the user's Desktop contacts
            }
            else
            {
                gridEWSConfig.Visibility = Visibility.Visible;
                configGrid.Visibility = Visibility.Visible;

                if (SettingsWindow.InstanceCount == 0)
                {
                    _configWindow = new SettingsWindow();
                    _configWindow.Show();
                }
            }
        }

        void LoadUserDetails()
        {
            gridMyPersona.Visibility = Visibility.Visible;

            personaUser.SipUri = _mySipUri;
            tbUserDisplayName.Text = StringFunctions.TrimGadgetHeaderDisplayName(_myDisplayName);
        }

        void LoadFav5()
        {
            gridFav5.Visibility = Visibility.Visible;

            FavoriteElementCollection fav5 = GadgetConfigurationManager.GetCurrentFav5List();
            List<Contact> fav5Contacts = null;

            if (fav5.Count > 0)
            {
                fav5Contacts = new List<Contact>();
                foreach (FavoriteElement fav in fav5)
                {
                    fav5Contacts.Add(
                        new Contact
                        {
                            SipUri = fav.ContactID,
                            DisplayName = fav.DisplayName,
                            IsPhoneNumber = fav.IsPhoneNumber,
                            DefaultAction = (ClickAction)Enum.Parse(typeof(ClickAction), fav.DefaultClickAction)
                        });
                }
            }

            listBoxFav5.ItemsSource = fav5Contacts;
        }

        void LoadTop5()
        {
            this.gridTop5.Visibility = Visibility.Visible;

            FavoriteElementCollection top5 = GadgetConfigurationManager.GetCurrentTop5List();
            if (top5.Count > 0)
            {
                List<Contact> top5Contacts = new List<Contact>();
                foreach (FavoriteElement contact in top5)
                {
                    top5Contacts.Add(
                        new Contact(
                            contact.ContactID,
                            StringFunctions.TrimGadgetPersonaDisplayName(contact.DisplayName)));
                }

                listBoxTop5.ItemsSource = top5Contacts;
            }
        }

        void ToggleVoicemailNotification(UnreadVoicemailEventArgs args)
        {
            if (args.UnreadVMs > 0)
            {
                this.voiceMailNotify.Source = Application.Current.Resources["VMUnreadSmall"] as DrawingImage;
            }
            else
            {
                this.voiceMailNotify.Source = Application.Current.Resources["VMSmall"] as DrawingImage;
            }
        }

        #endregion

        #region Unloading

        void Gadget_Unloaded(object sender, RoutedEventArgs e)
        {
            GadgetEventManager.Instance.UnreadVoicemailEvent -= new EventHandler(Instance_UnreadVoicemailEvent);
            GadgetEventManager.Instance.EWSConfiguredEvent -= new EventHandler(Instance_EWSConfiguredEvent);
            GadgetEventManager.Instance.Fav5ChangedEvent -= new EventHandler(Instance_Fav5ChangedEvent);
            GadgetEventManager.Instance.Top5ChangedEvent -= new EventHandler(Instance_Top5ChangedEvent);
            GadgetEventManager.Instance.GadgetSignInEvent -= new EventHandler(Instance_GadgetSignInEvent);
            GadgetEventManager.Instance.VoicemailEnabledEvent -= new EventHandler(Instance_VoicemailEnabledEvent);
            GadgetEventManager.Instance.DesktopContactChangedEvent -= new EventHandler(Instance_DesktopContactChangedEvent);

            this.expanderFav5.Collapsed -= new RoutedEventHandler(expanderFav5_Collapsed);
            this.expanderFav5.Expanded -= new RoutedEventHandler(expanderFav5_Expanded);
            this.expanderTop5.Collapsed -= new RoutedEventHandler(expanderTop5_Collapsed);
            this.expanderTop5.Expanded -= new RoutedEventHandler(expanderTop5_Expanded);
            this.configGrid.MouseDown -= new MouseButtonEventHandler(configGrid_MouseDown);
            this.phoneLayer.MouseDown -= new MouseButtonEventHandler(phoneLayer_MouseDown);
            this.listBoxTop5.PreviewMouseDown -= new MouseButtonEventHandler(listBoxTop5_PreviewMouseDown);
            this.listBoxFav5.PreviewMouseDown -= new MouseButtonEventHandler(listBoxFav5_PreviewMouseDown);
            this.listBoxTop5.MouseDoubleClick -= new MouseButtonEventHandler(listBoxTop5_MouseDoubleClick);
            this.listBoxFav5.MouseDoubleClick -= new MouseButtonEventHandler(listBoxFav5_MouseDoubleClick);

            SystemEvents.PowerModeChanged -= new PowerModeChangedEventHandler(SystemEvents_PowerModeChanged);
        }

        void UnloadGadget()
        {
            BackgroundWorker workerUnloadGadget = new BackgroundWorker();
            workerUnloadGadget.WorkerReportsProgress = false;
            workerUnloadGadget.WorkerSupportsCancellation = false;

            SettingsEWS curSettings = Gadget.CurrentSettings;

            workerUnloadGadget.DoWork += delegate(object s, DoWorkEventArgs args)
            {
                GenericUIDelegate delegateUnloadGadget = new GenericUIDelegate(UnloadGadget_DoWork);
                this.Dispatcher.Invoke(DispatcherPriority.Input, delegateUnloadGadget);
            };

            workerUnloadGadget.RunWorkerAsync();
        }

        void UnloadGadget_DoWork()
        {
            configGrid.Visibility = Visibility.Collapsed;
            phoneLayer.Visibility = Visibility.Collapsed;
            gridFav5.Visibility = Visibility.Collapsed;
            gridTop5.Visibility = Visibility.Collapsed;
            gridMyPersona.Visibility = Visibility.Collapsed;

            gridSignIn.Visibility = Visibility.Visible;

            GadgetEventManager.Instance.OnApplicationUnloadedEvent(EventArgs.Empty);

            this.UserDesktopContacts.Clear();
        }

        #endregion

        #region GadgetEventManager Event Handlers

        void Instance_UnreadVoicemailEvent(object sender, EventArgs e) 
        {
            UnreadVoicemailEventArgs args = (UnreadVoicemailEventArgs)e;
            ToggleVoicemailNotification(args);
        } 

        void Instance_EWSConfiguredEvent(object sender, EventArgs e) 
        {
            InitializeGadget();
        } 

        void Instance_Fav5ChangedEvent(object sender, EventArgs e) 
        {
            Fav5ChangedEventArgs fav5ChangedEventArgs = e as Fav5ChangedEventArgs;

            BackgroundWorker workerFav5Changed = new BackgroundWorker();
            workerFav5Changed.WorkerReportsProgress = false;
            workerFav5Changed.WorkerSupportsCancellation = false;

            Fav5ChangedDelegate delegateFav5Changed = null;

            workerFav5Changed.DoWork += delegate(object s, DoWorkEventArgs args)
            {
                if (fav5ChangedEventArgs.Operation == Fav5Operation.Add)
                    delegateFav5Changed = new Fav5ChangedDelegate(AddFav5Contact);
                else if (fav5ChangedEventArgs.Operation == Fav5Operation.Modify)
                    delegateFav5Changed = new Fav5ChangedDelegate(ModifyFav5Contact);
                else if (fav5ChangedEventArgs.Operation == Fav5Operation.Remove)
                    delegateFav5Changed = new Fav5ChangedDelegate(RemoveFav5Contact);

                this.Dispatcher.Invoke(DispatcherPriority.Background, delegateFav5Changed, fav5ChangedEventArgs);
            };

            workerFav5Changed.RunWorkerAsync();
        } 

        void Instance_Top5ChangedEvent(object sender, EventArgs e) 
        {
            BackgroundWorker workerLoadTop5 = new BackgroundWorker();
            workerLoadTop5.WorkerReportsProgress = false;
            workerLoadTop5.WorkerSupportsCancellation = false;

            workerLoadTop5.DoWork += delegate(object s, DoWorkEventArgs args)
            {
                LoadTop5Delegate delegateLoadTop5 = new LoadTop5Delegate(LoadTop5);
                this.Dispatcher.Invoke(DispatcherPriority.Background, delegateLoadTop5);
            };

            workerLoadTop5.RunWorkerAsync();
        }

        void Instance_GadgetSignInEvent(object sender, EventArgs e)
        {
            InitializeGadget();
        } 

        void Instance_VoicemailEnabledEvent(object sender, EventArgs e)
        {
            SetupTimer_Voicemail();
        }

        void Instance_DesktopContactChangedEvent(object sender, EventArgs e)
        {
            DesktopContactChangedEventArgs desktopContactChangedEventArgs = e as DesktopContactChangedEventArgs;

            BackgroundWorker workerDesktopContactsChanged = new BackgroundWorker();
            workerDesktopContactsChanged.WorkerReportsProgress = false;
            workerDesktopContactsChanged.WorkerSupportsCancellation = false;

            DesktopContactsChangedDelegate delegateDesktopContactsChanged = null;

            workerDesktopContactsChanged.DoWork += delegate(object s, DoWorkEventArgs args)
            {
                if (desktopContactChangedEventArgs.Operation == DesktopContactOperation.Add)
                    delegateDesktopContactsChanged = new DesktopContactsChangedDelegate(AddDesktopContact);
                else if (desktopContactChangedEventArgs.Operation == DesktopContactOperation.Modify)
                    delegateDesktopContactsChanged = new DesktopContactsChangedDelegate(ModifyDesktopContact);
                else if (desktopContactChangedEventArgs.Operation == DesktopContactOperation.Open)
                    delegateDesktopContactsChanged = new DesktopContactsChangedDelegate(OpenDesktopContact);
                //else if (desktopContactChangedEventArgs.Operation == DesktopContactOperation.Remove)
                //    delegateDesktopContactsChanged = new DesktopContactsChangedDelegate(RemoveDesktopContact);

                if (delegateDesktopContactsChanged != null)
                    this.Dispatcher.Invoke(DispatcherPriority.Background,
                        delegateDesktopContactsChanged, desktopContactChangedEventArgs);
            };

            workerDesktopContactsChanged.RunWorkerAsync();
        }

        #endregion

        #region Power Management

        void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
        {
            if (e.Mode == PowerModes.Suspend || e.Mode == PowerModes.Resume)
            {
                this.UnloadGadget();
            }
        }

        #endregion

        #region Fav5 Contacts CRUD

        private void AddFav5Contact(Fav5ChangedEventArgs args)
        {
            int orderKey = args.OrderKey;
            string primarySMTP = args.SIPUri; 
            string sipUri = default(string);
            string displayName = default(string);
            string defaultAction = default(string);
            bool isPhoneNumber = default(bool);

            if (StringFunctions.IsPhoneNumber(primarySMTP)) // Is the entry a phone number?
            {
                sipUri = primarySMTP;
                displayName = StringFunctions.TrimGadgetPersonaDisplayName(primarySMTP);
                defaultAction = ClickAction.PhoneCall.ToString();
                isPhoneNumber = true;
            }
            else
            {
                ResolveContact(primarySMTP, out sipUri, out displayName);

                if (!String.IsNullOrEmpty(sipUri) && !String.IsNullOrEmpty(displayName))
                {
                    displayName = StringFunctions.TrimGadgetPersonaDisplayName(displayName);
                    defaultAction = ClickAction.InstantMessage.ToString();
                    isPhoneNumber = false;
                }
                else
                {
                    GadgetEventManager.Instance.OnMismatchedSipEvent(new MismatchedSIPEventArgs(orderKey, primarySMTP));
                    return;
                }   
            }

            GadgetConfigurationManager.AddFavorite5(orderKey, sipUri, displayName, defaultAction, isPhoneNumber);
            GadgetEventManager.Instance.OnFav5ContactDetailsConfirmedEvent(
                new Fav5ChangedEventArgs(orderKey, sipUri, displayName, isPhoneNumber, true));

            LoadFav5();
        }

        private void ModifyFav5Contact(Fav5ChangedEventArgs args)
        {
            LoadFav5();
        }

        private void RemoveFav5Contact(Fav5ChangedEventArgs args)
        {
            LoadFav5();
        }

        #endregion

        #region Desktop Contacts CRUD

        void AddDesktopContact(DesktopContactChangedEventArgs e) 
        {
            DesktopContact desktopContact = e.AffectedContact;

            string dropString = desktopContact.SipUri;
            string sipUri = default(string);
            string displayName = default(string);
            bool visible = desktopContact.Visible;

            ResolveContact(dropString, out sipUri, out displayName);

            if (String.IsNullOrEmpty(sipUri) || String.IsNullOrEmpty(displayName))
            {
                // Couldn't match to an Office Communicator contact
                //
                RaiseNotifyDesktopContactChangeEvent(
                    DesktopContactOperation.Mismatch,
                    new DesktopContact() 
                        { 
                            ContactId = desktopContact.ContactId,
                            DisplayName = null,
                            SipUri = dropString,
                            Visible = visible
                        });
                return;
            }

            GadgetConfigurationManager.AddDesktopContact(desktopContact.ContactId, sipUri, displayName, visible);

            var contactToDisplay = new DesktopContact()
                {
                    ContactId = desktopContact.ContactId,
                    DisplayName = displayName,
                    SipUri = sipUri,
                    Visible = visible
                };

            RaiseNotifyDesktopContactChangeEvent(DesktopContactOperation.Add, contactToDisplay);

            ShowDesktopContactWindow(contactToDisplay);
        }

        void ModifyDesktopContact(DesktopContactChangedEventArgs e) { }

        void OpenDesktopContact(DesktopContactChangedEventArgs e) 
        {
            ShowDesktopContactWindow(e.AffectedContact);
        }

        void RemoveDesktopContact(DesktopContactChangedEventArgs e) 
        {
            CloseDesktopContactWindow(e.AffectedContact.SipUri);
        }

        void ShowDesktopContactWindow(DesktopContact desktopContact)
        {
            if (!String.IsNullOrEmpty(desktopContact.SipUri)) // Should always be true
            {
                DesktopContactWindow desktopContactWindow = new DesktopContactWindow();

                desktopContactWindow.ContactToDisplay = desktopContact;
                desktopContactWindow.Show();

                UserDesktopContacts.Add(desktopContact.SipUri,
                    new WindowInteropHelper(desktopContactWindow).Handle);
            }
        }

        void CloseDesktopContactWindow(string sipUri)
        {
            User32Utility.CloseWindow(Gadget.Instance.UserDesktopContacts[sipUri]);
            UserDesktopContacts.Remove(sipUri);
        }

        void LoadDesktopContactWindows()
        {
            if (UserDesktopContacts == null)
                UserDesktopContacts = new Dictionary<String, IntPtr>();

            if (PresenceHistoryWindows == null)
                PresenceHistoryWindows = new List<string>();

            foreach (DesktopContactElement desktopContact in GadgetConfigurationManager.GetDesktopContacts())
            {
                if (desktopContact.Visible)
                {
                    ShowDesktopContactWindow(
                        new DesktopContact()
                        {
                            ContactId = desktopContact.ContactId,
                            SipUri = desktopContact.SipUri,
                            DisplayName = desktopContact.DisplayName,
                            Visible = desktopContact.Visible
                        });
                }
            }
        }

        void UpdateUserDesktopContacts(IDictionary<String, IntPtr> desktopContacts) { }

        void RaiseNotifyDesktopContactChangeEvent(
            DesktopContactOperation desktopContactOperation, DesktopContact desktopContact)
        {
            GadgetEventManager.Instance.OnNotifyDesktopContactChangedEvent(
                new DesktopContactChangedEventArgs()
                {
                    Operation = desktopContactOperation,
                    AffectedContact = desktopContact
                });
        }

        void ProcessContactStatusChange_DoWork(object pMContact)
        {
            IMessengerContactAdvanced contact = pMContact as IMessengerContactAdvanced;

            if (contact != null)
            {
                if (UserDesktopContacts.ContainsKey(contact.SigninName))
                {
                    Object[] cProperties = contact.PresenceProperties as Object[];
                    MISTATUS status = (MISTATUS)cProperties[(int)PRESENCE_PROPERTY.PRESENCE_PROP_MSTATE];

                    GadgetEventManager.Instance.OnPresenceStatusChangedEvent(
                        new PresenceStatusChangedEventArgs()
                            {
                                SipUri = contact.SigninName,
                                Status = status
                            });
                }
            }

            Marshal.ReleaseComObject(contact);
            contact = null;
        }

        #endregion

        #region Other UI Functionality

        void btnSignIn_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                CommunicatorSignIn();

                gridSignIn.Visibility = Visibility.Collapsed;
                gridSignInError.Visibility = Visibility.Collapsed;

                InitializeGadget();
            }
            catch (Exception exception)
            {
                gridSignIn.Visibility = Visibility.Visible;
                gridSignInError.Visibility = Visibility.Visible;

                if (exception.Message == ERROR_COMMUNICATOR_NOT_INSTALLED)
                {
                    tbSignInError.Text = "Microsoft Office Communicator is required for this gadget.";
                }
            }
        }

        void phoneLayer_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if (VoiceMailWindow.InstanceCount == 0)
            {
                _voiceMailWindow = new VoiceMailWindow();
                _voiceMailWindow.Show();
            }        
        }

        void configGrid_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if (SettingsWindow.InstanceCount == 0)
            {
                _configWindow = new SettingsWindow();
                _configWindow.Show();
            }
        }

        void expanderTop5_Expanded(object sender, RoutedEventArgs e)
        {
            expanderFav5_Collapsed(null, null);
        }

        void expanderTop5_Collapsed(object sender, RoutedEventArgs e)
        {
            expanderFav5_Expanded(null, null);
        }

        void expanderFav5_Expanded(object sender, RoutedEventArgs e)
        {
            //Swap Expander Positions
            this.expanderTop5.IsExpanded = false;
            this.expanderTop5.VerticalAlignment = VerticalAlignment.Bottom;
            this.expanderTop5.Margin = new Thickness(0, 0, 0, 0);

            this.expanderFav5.IsExpanded = true;
            this.expanderFav5.VerticalAlignment = VerticalAlignment.Top;
            this.expanderFav5.Margin = new Thickness(0);
        }

        void expanderFav5_Collapsed(object sender, RoutedEventArgs e)
        {
            //Swap Expander Positions
            this.expanderTop5.IsExpanded = true;
            this.expanderTop5.VerticalAlignment = VerticalAlignment.Top;
            this.expanderTop5.Margin = new Thickness(0);

            this.expanderFav5.IsExpanded = false;
            this.expanderFav5.VerticalAlignment = VerticalAlignment.Bottom;
            this.expanderFav5.Margin = new Thickness(0, 0, 0, 0);
        }

        void listBoxFav5_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            Point location = Mouse.GetPosition(this);   // Get the mouse position
            VisualTreeHelper.HitTest(this, null,        // Hit test to find Fav5 slot
                new HitTestResultCallback(HitTestCallBackFav5),
                new PointHitTestParameters(location));

            if (listBoxFav5.SelectedIndex != -1)
            {
                Contact selectedContact = (Contact)listBoxFav5.SelectedItem;
                
                if (!selectedContact.IsPhoneNumber)
                    PerformAdvancedContactAction(selectedContact, System.Windows.Forms.Control.ModifierKeys);
            }
        }

        void listBoxFav5_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            if (listBoxFav5.SelectedIndex != -1)
            {
                Contact selectedContact = (Contact)listBoxFav5.SelectedItem;

                if (selectedContact.IsPhoneNumber)
                    DialPhoneNumber(selectedContact.SipUri);
                else
                    StartConversation(selectedContact.SipUri, selectedContact.DefaultAction);
            }
        }

        void listBoxTop5_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            Point location = Mouse.GetPosition(this);   // Get the mouse position
            VisualTreeHelper.HitTest(this, null,        // Hit test to find Top5 slot
                new HitTestResultCallback(HitTestCallBackTop5),
                new PointHitTestParameters(location));

            if (listBoxTop5.SelectedIndex != -1)
            {
                PerformAdvancedContactAction((Contact)listBoxTop5.SelectedItem, System.Windows.Forms.Control.ModifierKeys);
            }
        }

        void listBoxTop5_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            if (listBoxTop5.SelectedIndex != -1)
            {
                Contact selectedContact = (Contact)listBoxTop5.SelectedItem;
                StartConversation(selectedContact.SipUri, ClickAction.InstantMessage);
            }
        }

        HitTestResultBehavior HitTestCallBackFav5(HitTestResult result)
        {
            if (VisualTreeHelper.GetParent(result.VisualHit) is ListBoxItem)
            {
                ItemContainerGenerator generator = this.listBoxFav5.ItemContainerGenerator;
                ListBoxItem fav5Item = (ListBoxItem)VisualTreeHelper.GetParent(result.VisualHit);
                int rowIndex = generator.IndexFromContainer(fav5Item);
                Contact fav5 = (Contact)listBoxFav5.Items[rowIndex];
                listBoxFav5.SelectedItem = fav5;
            }

            return HitTestResultBehavior.Continue;
        }

        HitTestResultBehavior HitTestCallBackTop5(HitTestResult result)
        {
            if (VisualTreeHelper.GetParent(result.VisualHit) is ListBoxItem)
            {
                ItemContainerGenerator generator = this.listBoxTop5.ItemContainerGenerator;
                ListBoxItem top5Item = (ListBoxItem)VisualTreeHelper.GetParent(result.VisualHit);
                int rowIndex = generator.IndexFromContainer(top5Item);
                Contact top5 = (Contact)listBoxTop5.Items[rowIndex];
                listBoxTop5.SelectedItem = top5;
            }

            return HitTestResultBehavior.Continue;
        }

        #endregion

        #region Communicator Automation

        private Messenger _communicator;                    // interact with the main communicator instance
        private IMessengerAdvanced _communicatorAdvanced;   // use for advanced communicator features
        private IMessengerContactResolution _resolver;      // use for contact name resolution
        private string _mySipUri;                           // signed in user's sip uri
        private static string _myDisplayName;               // signed in user's display name
        private bool _connected = false;                    // connected to communicator
        private long _windowHandle;                         // handle to the current conversation window
        private static int _communicatorUpAndRunning = -1;  // is MOC up and running?
       
        void CommunicatorSignIn()
        {
            // Sign into the running instance of Office Communicator
            // Create an instance of Messenger and IMessengerAdvanced to be used by other functionality
            // Wire up Communicator events

            if (_communicator != null && _connected && !String.IsNullOrEmpty(_mySipUri))
                return;

            try
            {
                _communicatorUpAndRunning = 0;
                _communicatorUpAndRunning = Convert.ToInt32(Microsoft.Win32.Registry.CurrentUser
                    .OpenSubKey("Software").OpenSubKey("IM Providers").OpenSubKey("Communicator").GetValue("UpAndRunning", 1));

                if (_communicatorUpAndRunning != 2)
                {
                    throw (new Exception(ERROR_COMMUNICATOR_NOT_INSTALLED));
                }

                _communicator = new CommunicatorAPI.MessengerClass();
                _communicatorAdvanced = _communicator as IMessengerAdvanced;
                _resolver = _communicator as IMessengerContactResolution;

                _communicator.OnSignin += new DMessengerEvents_OnSigninEventHandler(communicator_OnSignin);
                _communicator.OnSignout += new DMessengerEvents_OnSignoutEventHandler(communicator_OnSignout);
                _communicator.OnIMWindowCreated += new DMessengerEvents_OnIMWindowCreatedEventHandler(communicator_OnIMWindowCreated);
                _communicator.OnIMWindowDestroyed += new DMessengerEvents_OnIMWindowDestroyedEventHandler(communicator_OnIMWindowDestroyed);
                _communicator.OnContactStatusChange += new DMessengerEvents_OnContactStatusChangeEventHandler(communicator_OnContactStatusChange);
                _communicator.OnAppShutdown += new DMessengerEvents_OnAppShutdownEventHandler(communicator_OnAppShutdown);

                _communicator.AutoSignin();
                _communicatorAdvanced.AutoSignin();

                _mySipUri = _communicator.MySigninName;
                _myDisplayName = _communicator.MyFriendlyName;
            }
            catch (Exception exception)
            {
                throw exception;
            }
        }

        void CommunicatorSignOut()
        {
            try
            {
                if (!_connected)
                    return;

                if (_communicator == null)
                    return;

                _communicator.Signout();
                _communicatorAdvanced.Signout();

                _connected = false;
            }
            catch (COMException comException)
            {
                throw comException;
            }
            catch (Exception exception)
            {
                throw exception;
            }
        }

        void communicator_OnSignin(int hr)
        {
            try
            {
                if (hr != 0)
                {
                    return;
                }

                _connected = true;

                this.Dispatcher.BeginInvoke(
                    System.Windows.Threading.DispatcherPriority.Normal,
                    new GenericUIDelegate(
                        delegate
                        {
                            btnSignIn_Click(this, new RoutedEventArgs());
                        }));
            }
            catch (COMException comException)
            {
                throw comException;
            }
            catch (Exception exception)
            {
                throw (exception);
            }
        }

        void communicator_OnSignout()
        {
            try
            {
                _connected = false;

                _communicator.OnSignin -= new DMessengerEvents_OnSigninEventHandler(communicator_OnSignin);
                _communicator.OnSignout -= new DMessengerEvents_OnSignoutEventHandler(communicator_OnSignout);
                _communicator.OnIMWindowCreated -= new DMessengerEvents_OnIMWindowCreatedEventHandler(communicator_OnIMWindowCreated);
                _communicator.OnIMWindowDestroyed -= new DMessengerEvents_OnIMWindowDestroyedEventHandler(communicator_OnIMWindowDestroyed);
                _communicator.OnContactStatusChange -= new DMessengerEvents_OnContactStatusChangeEventHandler(communicator_OnContactStatusChange);
                _communicator.OnAppShutdown -= new DMessengerEvents_OnAppShutdownEventHandler(communicator_OnAppShutdown);

                Marshal.ReleaseComObject(_communicator);
                _communicator = null;

                Marshal.ReleaseComObject(_communicatorAdvanced);
                _communicatorAdvanced = null;

                Marshal.ReleaseComObject(_resolver);
                _resolver = null;

                UnloadGadget(); 
            }
            catch (COMException comException)
            {
                throw comException;
            }
            catch (Exception exception)
            {
                throw (exception);
            }
        }

        void communicator_OnIMWindowCreated(object pIMWindow)
        {
            /// <summary>
            /// Runs when a conversation window is created
            /// Can be triggered either conversations initiated in Office Communicator, or the gadget itself
            /// 
            /// Conversations initiated from the gadget are triggered from the StartConversation method.
            ///  StartConversation sets the CONVERSATION_TYPE, usage statistics are captured there
            ///  Calling StartConversation will trigger the communicator_OnIMWindowCreated event.
            ///  But there is no access to a window's CONVERSATION_TYPE in communicator_OnIMWindowCreated.
            /// In this event, only capture usage statistics for conversations not initiated by the gadget.
            ///  For those conversations default CONVERSATION_TYPE to CONVERSATION_TYPE_IM
            /// </summary>
            /// <param name="pIMWindow">IMessengerConversationWndAdvanced COM object</param>
            try
            {
                IMessengerConversationWndAdvanced imWindow = (IMessengerConversationWndAdvanced)pIMWindow;
                IMessengerContacts contacts = (IMessengerContacts)imWindow.Contacts;

                if (imWindow.HWND == _windowHandle)
                {
                    // Outgoing conversation - initiated by the gadget
                    //  Usage statistics have already been captured in StartConversation
                }
                else
                {
                    // Incoming conversation OR conversation not initiated by the gadget
                    _windowHandle = imWindow.HWND;

                    foreach (IMessengerContact contact in contacts)
                    {
                        if (!contact.IsSelf) // Default incoming conversations to IM - can't detect their actual type
                            RecordUsageStatistic(contact.SigninName, CONVERSATION_TYPE.CONVERSATION_TYPE_IM);
                    }
                }
            }
            catch (COMException comException)
            {
                throw comException;
            }
            catch (Exception exception)
            {
                throw (exception);
            }
        }

        void communicator_OnIMWindowDestroyed(object pIMWindow)
        {
            try
            {
                Marshal.ReleaseComObject(pIMWindow);
            }
            catch (COMException comException)
            {
                throw comException;
            }
            catch (Exception exception)
            {
                throw (exception);
            }
        }

        void communicator_OnContactStatusChange(object pMContact, MISTATUS mStatus)
        {
            BackgroundWorker workerProcessContactStatusChange = new BackgroundWorker();
            workerProcessContactStatusChange.WorkerReportsProgress = false;
            workerProcessContactStatusChange.WorkerSupportsCancellation = false;

            workerProcessContactStatusChange.DoWork += delegate(object s, DoWorkEventArgs args)
            {
                ProcessContactStatusChangeDelegate delegateProcessContactStatusChange =
                    new ProcessContactStatusChangeDelegate(ProcessContactStatusChange_DoWork);
                this.Dispatcher.Invoke(DispatcherPriority.Input, 
                    delegateProcessContactStatusChange, pMContact);
            };

            workerProcessContactStatusChange.RunWorkerAsync();
        }

        void communicator_OnAppShutdown()
        {
            communicator_OnSignout(); // Call communicator_OnSignout to clean up objects
        }  

        IMessengerContact FindContact(string userId)
        {
            /// <summary>
            /// If a user adds a distribution list, e.g. ContosoEmployees to Communicator, 
            ///  the contacts in that list aren't actually in IMessenger.MyContacts.
            /// This method checks the user's IMessenger.MyContacts list first to find the contact.
            /// If the contact doesn't exist there, it does a lookup using Communicator's SIP Provider.
            /// NOTE: Contacts that are tagged are automatically added to the user's Communicator contact list
            /// </summary>
            /// <param name="userId">Find Contact using their email address or sip</param>
            /// <returns>IMessengerContact Contact COM object</returns>
            try
            {
                IMessengerContact contact = null;
                try
                {
                    // Search the user's contact list in Communicator
                    contact = (IMessengerContact)_communicator.GetContact(userId, string.Empty);
                }
                catch
                {
                    contact = null;
                }

                // Try Communicator's SIP Provider
                if (contact == null || contact.Status == MISTATUS.MISTATUS_UNKNOWN)
                {
                    try
                    {
                        contact = (IMessengerContact)_communicator.GetContact(userId, _communicator.MyServiceId);
                        return contact;
                    }
                    catch
                    {
                        contact = null;
                        return contact;
                    }
                }
                else
                {
                    return contact;
                }
            }
            catch (COMException comException)
            {
                throw comException;
            }
            catch (Exception exception)
            {
                throw exception;
            }
        }

        void GetContactDetails(string sipUri, out string displayName)
        {
            IMessengerContact contact;

            try
            {
                displayName = null;
                contact = null;

                try
                {
                    contact = (IMessengerContact)_communicator.GetContact(sipUri, string.Empty);
                }
                catch
                {
                    contact = null;
                }

                // Try Communicator's SIP Provider
                if (contact == null || contact.Status == MISTATUS.MISTATUS_UNKNOWN)
                {
                    try
                    {
                        contact = (IMessengerContact)_communicator.GetContact(sipUri, _communicator.MyServiceId);
                        if (sipUri.ToLower() == contact.FriendlyName.ToLower())
                            contact = null;
                    }
                    catch
                    {
                        contact = null;
                    }
                }

                try
                {
                    if (contact != null)
                    {
                        displayName = contact.FriendlyName;
                        Marshal.ReleaseComObject(contact);
                        return;
                    }
                }
                catch
                {
                    contact = null;
                }
            }
            catch (Exception exception)
            {
                contact = null;
                Marshal.ReleaseComObject(contact);
                throw exception;
            }
        }

        void GetContactDetails(string primarySMTP, out string sipUri, out string displayName)
        {
            /// <summary>
            /// When dragging out of a Contact list in Communicator, the value is the Contact's primary SMTP address.
            /// This can possibly be different than the Contact's SIP.
            /// </summary>

            // There's a lot of logic here that's in try/catch blocks. We can't anticipate what the user will enter 
            //  when attempting to add a contact. Additionally, the GetContact method seems to "find" something
            //  regardless of what you pass in. Accessing properties may thus raise an exception, just catch
            //  the exception and set the contact to null.

            IMessengerContact contact;

            try
            {
                sipUri = null;
                displayName = null;
                contact = null;

                try
                {
                    contact = (IMessengerContact)_communicator.GetContact(primarySMTP, string.Empty);
                }
                catch
                {
                    contact = null;
                }

                // Try Communicator's SIP Provider
                if (contact == null || contact.Status == MISTATUS.MISTATUS_UNKNOWN)
                {
                    try
                    {
                        contact = (IMessengerContact)_communicator.GetContact(primarySMTP, _communicator.MyServiceId);
                        if (primarySMTP.ToLower() == contact.FriendlyName.ToLower())
                            contact = null;
                    }
                    catch
                    {
                        contact = null;
                    }
                }

                try
                {
                    if (contact != null)
                    {
                        sipUri = contact.SigninName;
                        displayName = contact.FriendlyName;
                        Marshal.ReleaseComObject(contact);
                        return;
                    }
                }
                catch
                {
                    contact = null;
                }
            }
            catch (Exception exception)
            {
                contact = null;
                Marshal.ReleaseComObject(contact);
                throw exception;
            }
        }

        void ResolveContact(string dropString, out string sipUri, out string displayName)
        {
            try
            {
                sipUri = null;
                displayName = null;

                try
                {
                    // Scenario 1: User enters a Sip Uri
                    //  If so, GetContactDetails will return a display name
                    //
                    GetContactDetails(dropString, out displayName);
                }
                catch
                {
                    sipUri = null;
                }

                if (String.IsNullOrEmpty(displayName)) // Not a match based on Sip Uri
                {
                    try
                    {
                        // Scenario 2: User enters an SMTP address
                        //  Try to resolve the SMTP address into a Sip Uri
                        //
                        sipUri = _resolver.ResolveContact(ADDRESS_TYPE.ADDRESS_TYPE_SMTP,
                            CONTACT_RESOLUTION_TYPE.CONTACT_RESOLUTION_CACHED_ONLY, dropString);
                    }
                    catch
                    {
                        sipUri = null;
                    }

                    if (String.IsNullOrEmpty(sipUri)) // Not a match based on SMTP Address
                    {
                        try
                        {
                            // Scenario 3: User enters a contact's display name
                            //  Try to resolve the Display Name into a Sip Uri
                            //
                            sipUri = _resolver.ResolveContact(ADDRESS_TYPE.ADDRESS_TYPE_DISPLAY_NAME,
                                CONTACT_RESOLUTION_TYPE.CONTACT_RESOLUTION_CACHED_ONLY, dropString);
                        }
                        catch
                        {
                            sipUri = null;
                        }
                    }

                    if (!String.IsNullOrEmpty(sipUri)) // Success - get the contact's Display Name
                    {
                        GetContactDetails(sipUri, out displayName);
                    }
                }
                else
                {
                    sipUri = dropString; // Scenario 1
                }
            }
            catch (Exception exception)
            {
                throw exception;
            }
        }

        void StartConversation(string sipUri, ClickAction clickAction)
        {
            CONVERSATION_TYPE conversationType = ConvertClickAction(clickAction);

            try
            {
                object[] participants = { sipUri };
                if (_communicatorAdvanced != null && _connected == true)
                {
                    object vConversationData = null;

                    if (clickAction == ClickAction.PhoneCall)
                    {
                        try
                        {
                            IMessengerContact contact = FindContact(sipUri);
                            string phoneNumber = contact.get_PhoneNumber(MPHONE_TYPE.MPHONE_TYPE_WORK);
                            Marshal.ReleaseComObject(contact); contact = null;
                            if (!String.IsNullOrEmpty(phoneNumber))
                                vConversationData = String.Format("tel:{0}", phoneNumber);
                            else
                                return;
                        }
                        catch
                        {
                            return;
                        }
                    }

                    object obj = _communicatorAdvanced.StartConversation(
                        conversationType,
                        participants,
                        null,
                        null,
                        null,
                        vConversationData);

                    _windowHandle = long.Parse(obj.ToString());
                    
                    RecordUsageStatistic(sipUri, conversationType);
                }
            }
            catch (COMException comException)
            {
                throw comException;
            }
            catch (Exception exception)
            {
                throw exception;
            }
        }

        void DialPhoneNumber(string phoneNumber)
        {
            try
            {
                object[] participants = { phoneNumber };
                if (_communicatorAdvanced != null && _connected == true)
                {
                    object vConversationData = String.Format("tel:{0}", phoneNumber);

                    object obj = _communicatorAdvanced.StartConversation(
                        CONVERSATION_TYPE.CONVERSATION_TYPE_PHONE,
                        participants,
                        null,
                        null,
                        null,
                        vConversationData);

                    _windowHandle = long.Parse(obj.ToString());
                }
            }
            catch
            {
                return; // 
            }
        }

        void InitiateEmail(string sipUri)
        {
            try
            {
                Process.Start(String.Format("mailto:{0}", sipUri));
            }
            catch
            {
                return;
            }
        }

        void RecordUsageStatistic(string sipUri, CONVERSATION_TYPE conversationType)
        {
            if (Convert.ToBoolean(ConfigurationManager.AppSettings["EnableOCSDKUsageTracking"]) == true)
            {
                GadgetConfigurationManager.ContactType contactType = ConvertConversationType(conversationType);
                
                bool tagged;
                bool teamMember = false;

                // Call FindContact to get an IMessengerContact object.
                //  Only doing this because we need the IsTagged property of IMessengerContactAdvanced.

                IMessengerContact contact = FindContact(sipUri);
                if (contact == null) // If we've gottent this far, contact can't really ever be null ...
                {
                    tagged = false;
                }
                else
                {
                    tagged = (contact as IMessengerContactAdvanced).IsTagged;
                }

                GadgetConfigurationManager.SetContactSettings(sipUri, contact.FriendlyName, contactType, 1, tagged, teamMember);
            }
        } 

        void PerformAdvancedContactAction(Contact contact, System.Windows.Forms.Keys keys)
        {
            const string KEYS_NONE = "NONE";
            const string KEYS_CONTROL = "CONTROL";
            const string KEYS_SHIFT = "SHIFT";

            string keysPressed = keys.ToString().ToUpper();

            if (keysPressed.ToUpper() == KEYS_NONE || String.IsNullOrEmpty(keysPressed))
                return;

            bool ctrlClicked = keysPressed.IndexOf(KEYS_CONTROL) >= 0;
            bool shiftClicked = keysPressed.IndexOf(KEYS_SHIFT) >= 0;

            if (!ctrlClicked && !shiftClicked)
                return;
            else
            {
                if (ctrlClicked && shiftClicked)
                    StartConversation(contact.SipUri, ClickAction.CommunicatorCall);
                else if (ctrlClicked)
                    StartConversation(contact.SipUri, ClickAction.PhoneCall);
                else if (shiftClicked)
                    InitiateEmail(contact.SipUri);
            }
        }

        GadgetConfigurationManager.ContactType ConvertConversationType(CONVERSATION_TYPE conversationType)
        {
            GadgetConfigurationManager.ContactType contactType;

            switch (conversationType)
            {
                case CONVERSATION_TYPE.CONVERSATION_TYPE_AUDIO:
                case CONVERSATION_TYPE.CONVERSATION_TYPE_VIDEO:
                    contactType = GadgetConfigurationManager.ContactType.CommunicatorCall;
                    break;
                case CONVERSATION_TYPE.CONVERSATION_TYPE_IM:
                    contactType = GadgetConfigurationManager.ContactType.IM;
                    break;
                case CONVERSATION_TYPE.CONVERSATION_TYPE_PHONE:
                    contactType = GadgetConfigurationManager.ContactType.PhoneCall;
                    break;
                default:
                    contactType = GadgetConfigurationManager.ContactType.None;
                    break;
            };

            return contactType;
        }

        CONVERSATION_TYPE ConvertClickAction(ClickAction clickAction)
        {
            CONVERSATION_TYPE conversationType;

            switch (clickAction)
            {
                case ClickAction.InstantMessage:
                case ClickAction.None:
                    conversationType = CONVERSATION_TYPE.CONVERSATION_TYPE_IM;
                    break;
                case ClickAction.CommunicatorCall:
                    conversationType = CONVERSATION_TYPE.CONVERSATION_TYPE_AUDIO;
                    break;
                case ClickAction.PhoneCall:
                    conversationType = CONVERSATION_TYPE.CONVERSATION_TYPE_PHONE;
                    break;
                case ClickAction.VideoCall:
                    conversationType = CONVERSATION_TYPE.CONVERSATION_TYPE_VIDEO;
                    break;
                default:
                    conversationType = CONVERSATION_TYPE.CONVERSATION_TYPE_IM;
                    break;
            };

            return conversationType;
        }

        #endregion

        #region Timers

        private void SetupTimers()
        {
            SetupTimer_Voicemail();
            SetupTimer_EmailCounts();
        } 

        #region Voicemail

        private delegate void RaiseUnreadVoicemailEventDelegate(UnreadVoicemailEventArgs e);
        private Delegate raiseUnreadVoicemailEvent;
        private int _numUnreadVMs;

        private void RaiseUnreadVoicemailEvent(UnreadVoicemailEventArgs e)
        {
            GadgetEventManager.Instance.OnUnreadVoicemailEvent(e);
        } 

        public void SetupTimer_Voicemail()
        {
            // We only want to initialize the Voicemail timer if Voicemail is actually enabled.
            //  When this method first runs, we don't know yet if Voicemail is enabled.
            //  It will be the responsibility of the Voicemail window to set up the timer.
            //  The Voicemail window can do this through the Instance property of the Gadget window.

            if (_curSettings.VoicemailEnabled.HasValue)
            {
                if ((bool)_curSettings.VoicemailEnabled)
                {
                    if (_timerVoicemail == null)
                    {
                        _timerVoicemail = new DispatcherTimer(
                            TimeSpan.FromMinutes(Convert.ToDouble(ConfigurationManager.AppSettings["TimerIntervalMinutes_Voicemail"])),
                            DispatcherPriority.Background, 
                            timerVoicemail_Tick, 
                            this.Dispatcher);

                        _timerVoicemail.Start();
                    }
                }
                else
                {
                    phoneLayer.Visibility = Visibility.Collapsed;
                }
            }
        }

        void timerVoicemail_Tick(object sender, EventArgs argz)
        {
            BackgroundWorker workerVoicemail = new BackgroundWorker();
            workerVoicemail.WorkerReportsProgress = false;
            workerVoicemail.WorkerSupportsCancellation = true;

            SettingsEWS curSettings = Gadget.CurrentSettings;

            EWSRequest ewsRequest = new EWSRequest(
                curSettings.EWSUrl,
                curSettings.DomainUser.Split(new char[] { '\\' })[1],
                curSettings.Password,
                curSettings.DomainUser.Split(new char[] { '\\' })[0]);

            workerVoicemail.DoWork += delegate(object s, DoWorkEventArgs args)
            {
                EWSVoicemail.GetNumberUnreadVoicemailsDelegate delegateGetNumberUnreadVoicemails =
                    new EWSVoicemail.GetNumberUnreadVoicemailsDelegate(EWSVoicemail.GetNumberUnreadVoicemails);
                _numUnreadVMs = Convert.ToInt32(this.Dispatcher.Invoke(
                    DispatcherPriority.Background, delegateGetNumberUnreadVoicemails, ewsRequest));
            };

            workerVoicemail.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
            {
                UnreadVoicemailEventArgs e = new UnreadVoicemailEventArgs(_numUnreadVMs);
                this.Dispatcher.Invoke(DispatcherPriority.Render, raiseUnreadVoicemailEvent, e);
            };

            workerVoicemail.RunWorkerAsync();
        } 

        #endregion

        #region Email Counts

        private IDictionary<string, ContactMessage> _contactCounts;

        public void SetupTimer_EmailCounts()
        {
            if (!String.IsNullOrEmpty(_curSettings.EWSUrl))
            {
                if (_timerEmailCounts == null)
                {
                    _timerEmailCounts = new DispatcherTimer(
                        TimeSpan.FromHours(Convert.ToDouble(ConfigurationManager.AppSettings["TimerIntervalHours_EmailCounts"])),
                        DispatcherPriority.Background,
                        timerEmailCounts_Tick,
                        this.Dispatcher);

                    _timerEmailCounts.Start();
                }
            }
        }

        private void timerEmailCounts_Tick(object sender, EventArgs argz)
        {
            BackgroundWorker workerEmailCounts = new BackgroundWorker();
            workerEmailCounts.WorkerReportsProgress = false;
            workerEmailCounts.WorkerSupportsCancellation = true;

            SettingsEWS curSettings = Gadget.CurrentSettings;

            EWSRequest ewsRequest = new EWSRequest(
                curSettings.EWSUrl,
                curSettings.DomainUser.Split(new char[] { '\\' })[1],
                curSettings.Password,
                curSettings.DomainUser.Split(new char[] { '\\' })[0]);

            workerEmailCounts.DoWork += delegate(object s, DoWorkEventArgs args)
            {

                EWSContact.GetEmailCountsDelegate delegateGetEmailCounts =
                    new EWSContact.GetEmailCountsDelegate(EWSContact.GetEmailCountTodayByContact);
                _contactCounts = (IDictionary<string, ContactMessage>) 
                    this.Dispatcher.Invoke(DispatcherPriority.Background, delegateGetEmailCounts, ewsRequest, _myDisplayName);
            };

            workerEmailCounts.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
            {
                EWSContact.ProcessEmailCountsDelegate delegateProcessEmailCounts =
                    new EWSContact.ProcessEmailCountsDelegate(EWSContact.ProcessEmailCounts);
                this.Dispatcher.Invoke(DispatcherPriority.Background, delegateProcessEmailCounts, _contactCounts);
            };

            workerEmailCounts.RunWorkerAsync();
        }

        #endregion

        #endregion
    }
}